March 95 - An Object-Oriented Approach to Hierarchical Lists
An Object-Oriented Approach to Hierarchical Lists
JAN BRUYNDONCKX
The article "Displaying Hierarchical Lists" in develop Issue 18 showed how to use the
List Manager to build and display lists of hierarchical data with triangular
"twist-down" buttons for expanding and collapsing sublists (similar to the ones the
Finder uses for displaying and hiding the contents of folders in a list view). In this
article, we take an object-oriented approach to implementing these and other custom
lists, using the PowerPlant application framework by Metrowerks. Using subclass
inheritance to build small classes on top of each other makes incremental development
easy and straightforward.
Recently, I found myself working on a project that needed hierarchical lists: a remote
debugger for a network-based software distribution application. The product,
FileWave, creates a "virtual disk" volume on the user's client machine and manages its
contents remotely from a central server. The debugger, called TheRaven, can retrieve
file and folder information from the client machine and display it in a Finder-like
hierarchical view (see Figure 1).
Martin Minow's article "Displaying Hierarchical Lists" (develop Issue 18) was an
excellent starting point, but Martin's implementation had some features that made it
unsuitable for my particular application. Most important, Martin built his
hierarchical lists completely in memory before displaying them -- not very practical
when working over a network. I could have modified Martin's code to remove that
restriction, but the result wouldn't have been very clean. Since we were using the
object-oriented PowerPlant application framework by Metrowerks, I decided to try to
develop an object-oriented implementation for hierarchical lists.
One of the advantages of object-oriented programming is that it enables you to build up
your implementation in incremental steps. PowerPlant's collection of small,
independent classes can be combined to build new classes with rich features, providing
a strong foundation for software development. And, of course, using PowerPlant gave
me an opportunity to try out the great Metrowerks CodeWarrior programming
environment.
This issue's CD contains some of the results of my development efforts. On it, you'll
find a collection of general-purpose classes for implementing lists with icons,
hierarchical lists, and other useful possibilities. You can use these as a basis for
developing more specialized subclasses of your own; the CD includes some examples of
those, as well.
Figure 1. TheRaven
The CD contains two project files: one for creating a 680x0 application and one for
the native PowerPC version. In both projects, only the main segment contains my own
code; all the other source files are taken from the PowerPlant development
framework.*
This article assumes that you understand the List Manager and how to use it, and that
you have at least a casual acquaintance with object-oriented programming in general
and C++ in particular.
BASIC BUILDING BLOCKS
In PowerPlant, everything that appears on the screen is apane, an instance of the
built-in class LPane. Like a view in MacApp, a pane can be anything from a plain
rectangle to a scroll bar, a picture, or a radio button. A control is a pane, as is an icon
button, a static text item, or a scrolling picture. Even LWindow, the class to which
windows themselves belong, is a subclass of LPane.
Typically, a window consists of an instance of class LWindow with one or more
subpanes derived from LPane. In our examples, our windows will have only one pane,
an instance of PowerPlant's built-in class LListBox. This type of pane uses the
Macintosh List Manager to display a list of objects. Each of our examples will define a
new subclass of LListBox with additional or modified properties and behavior. All it
takes to define such a class is to select an existing class, override its drawing method
(and maybe a couple of others), and possibly create a new resource template.
EASY LISTS
Our first example implements a simple window showing the list of words "One
through "Five" (see Figure 2). This may not seem like a big deal, but it's a good
illustration of the power of object- oriented programming.
Figure 2. An easy list
If we started from scratch, how many lines of code would this application take? Well,
we'd have to set up a menu, create a window, and then write an event loop to handle
dragging, window resizing, and so on. Add in the List Manager calls, and we'd be lucky
to do it all in fewer than 100 lines. With PowerPlant, all those details are handled for
us by the predefined class LApplication. All we need to do is define a subclass,
CListApp, with a menu command for creating our list window. One line of code in our
subclass's ObeyCommand method suffices to create the window:
LWindow::CreateWindow(EasyList_PPob, this);
This invokes a static method of class LWindow to create the window from a template
resource. EasyList_PPob is the resource ID; the exact description of the window is
contained in the resource, isolated from the code itself.
The resource definitions (Listing 1) give the details on the window's structure and
appearance. The familiar window template resource ('WIND') is accompanied by a
PowerPlant object resource ('PPob') giving extra information on the window and the
panes it encloses (see "'PPob' Resources"). The 'PPob' is simply a list of views and
panes, each specified with the keyword ObjectData. Panes can be nested to any depth,
with each new level delimited by the keywords BeginSubs and EndSubs. In our case,
the window view encloses just one pane, representing the list box.
An object-oriented application framework like PowerPlant is so powerful that these
two resources are all we need to describe our window and its list pane. With just one
line of code to create the window, we get all the standard behavior for free: dragging
and resizing the window, scrolling the list, and selecting items with the mouse. We can
have multiple windows with the same list, and can use the List Manager for
manipulations like adding or removing items.
But, of course, we won't stop there. In the following examples, we'll override the
standard behavior by creating a series of subclasses. The resources in each case will
be minor variations on the ones in Listing 1; the main difference is that we'll use a
subclass instead of one of the standard classes.
Listing 1. Resources for easy list
resource 'WIND' (EasyList_WIND, purgeable) {
{47, 17, 247, 317},
documentProc, // standard window with size box
visible, goAway,
0x0, // refCon
"Easy List",
noAutoCenter
};
resource 'PPob' (EasyList_PPob, purgeable) {{
ObjectData {Window {
EasyList_WIND,
regular, hasCloseBox, hasTitleBar, hasResize, hasSizeBox,
noZoom, hasShowNew, enabled, hasTarget, hasGetSelectClick,
noHideOnSuspend, noDelaySelect, hasEraseOnUpdate,
100, 100, // minimum width, height
screenSize, screenSize, // maximum width, height